#include "precompiled.h"
#include "RenderManager.h"
#include "Core.h"
#include "Geometry_Pool.h"
#include "Model.h"
#include "Texture.h"
#include "World_Manager.h"
#include "Player.h"

namespace RendererManager{

	std::vector<RenderTicket*> tickets, particle_tickets;


	// Receive a collection of render tickets. Tickets don't need to be sorted.
	void accept_renderTicket(RenderTicket &ticket){
		tickets.push_back( &ticket );
	}

	void accept_renderTicket_particle(RenderTicket &ticket){
		particle_tickets.push_back( &ticket );
	}

	void Clear_Tickets(){
		tickets.clear();
		particle_tickets.clear();
	}


	//................................................................................
	
	// Renders a single frame of game objects.
	void Draw_Frame( glm::mat4 &projview ){
		glClearColor(0.0f,0.3f,0.6f,1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		for( vector<RenderTicket*>::iterator it = tickets.begin(); it != tickets.end(); it++){

			// Send our transforms to the graphics device.
			ShaderPool::update_uniform_MVP_View( projview * ((*it)->bundle.transform->_model)  );

			// Clear any flash that shouldn't be applied.
			ShaderPool::update_uniform_flash( 0.0f );

			// Update object specific uniforms.
			if( (*it)->shadargs != 0 ){
				ShaderPool::update_uniform_flash( (*it)->shadargs->flash  );

			}

			// Make the order to render our model.
			Model *ref_model = gs_geompool->get_modelValue( (*it)->bundle.modelKey );
			glBindVertexArray(ref_model->vao()[0]);
			int num = ref_model->numIndices();
			glDrawRangeElements(GL_TRIANGLES,0, num,num , GL_UNSIGNED_SHORT, NULL);
			glBindVertexArray(0);
		}

		// Delete the shader arguments.
		for( vector<RenderTicket*>::iterator it = tickets.begin(); it != tickets.end(); it++){
			if( (*it)->shadargs != 0 ){
				delete (*it)->shadargs;
			}
			(*it)->shadargs = 0;
		}
	}
	// Draw our collection of render tickets.
	void Draw_renderTickets( glm::mat4 &projview ){
		ShaderPool::SetShader(ShaderPool::Shaders::NORMALDIFFUSE);
		Draw_Frame( projview );
	}

	void Draw_Points(Vertex_P* point_vertexBuffer, GLuint count, Texture* t ){

		//t->Bind(COLOR_TEXTURE_UNIT);
		/*if( t == 0 )
			default_texture->bind(COLOR_TEXTURE_UNIT);*/
		glPointSize( 1.0f );
		glEnableVertexAttribArray(0);

		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex_P), point_vertexBuffer); // position 
		glDrawArrays(GL_POINTS, 0, count );

		glDisableVertexAttribArray(0);
	}
	void Draw_Frame_Particles( glm::mat4 &projview ){
		for( int c = 0; c < particle_tickets.size(); c++ ){

			// Send our transforms to the graphics device.
			ShaderPool::update_uniform_MVP_View( projview );

			// Send over the camera position and View Projection.
			ShaderPool::update_uniform_CameraPosition( World_Manager::player->GetPosition() );
			ShaderPool::update_uniform_VP( projview );

			// Clear any flash that shouldn't be applied.
			ShaderPool::update_uniform_flash( 0.0f );

			// Update object specific uniforms.
			if( particle_tickets.at(c)->shadargs != 0 ){
				ShaderPool::update_uniform_flash( particle_tickets.at(c)->shadargs->flash  );

			}

			// Draw our particles.
			Draw_Points( particle_tickets.at(c)->ref_point_vertexBuffer, particle_tickets.at(c)->point_count, 0 );
		}

		// Delete the shader arguments.
		for( int c = 0; c < particle_tickets.size(); c++ ){
			if( particle_tickets.at( c )->shadargs != 0 ){
				delete particle_tickets.at( c )->shadargs;
			}
			particle_tickets.at( c )->shadargs = 0;
		}
	}
	void Draw_renderTickets_particles( glm::mat4 &projview ){
		ShaderPool::SetShader(ShaderPool::Shaders::BILLBOARD);
		Draw_Frame_Particles( projview );
	}
	
	void Draw(  glm::mat4 &projview ){
		Draw_renderTickets(projview);
		Draw_renderTickets_particles(projview);
	}
	//..........................................................................
	// Sort our tickets from front to back.
	bool myTicketSort_front ( RenderTicket* i, RenderTicket* j ){
		return ( i->distance < j->distance );
	}
	// Sort our tickets from back to front.
	bool myTicketSort_back ( RenderTicket* i, RenderTicket* j ){
		return ( i->distance > j->distance );
	}

	// Sorts drawing order.
	// If frontToBack = T, goes from front to back. Else goes from back to front.
	void SortTickets(bool frontToBack){
		if( frontToBack )
			sort( tickets.begin(), tickets.end(), myTicketSort_front );
		else
			sort( tickets.begin(), tickets.end(), myTicketSort_back );
	}
	//..........................................................................
}